package com.apanal.qlife.common.aop;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import com.apanal.qlife.common.cache.BaseCacheAspect;
import com.apanal.qlife.sys.model.User;
import com.apanal.qlife.sys.service.UserService;

/**
 * 用户缓存切面类<br>
 * 缓存实现:<br>
 * 1、username/email/mobilePhoneNumber------>id<br>
 * 2、id------->Model(User)
 * 
 * @author shuliangxing
 * 
 * @date 2015-10-8下午3:48:50
 */
@Component
@Aspect
public class UserCacheAspect extends BaseCacheAspect {

	/**
	 * 设置缓存名称
	 */
	public UserCacheAspect() {
		setCacheName("sys-userCache");
	}

	@Autowired
	private UserService userService;

	@Autowired
	private UserAuthCacheAspect userAuthCacheAspect;

	// id缓存key前缀
	private String idKeyPrefix = "id-";
	// 用户名缓存key前缀
	private String usernameKeyPrefix = "username-";
	// 邮件缓存key前缀
	private String emailKeyPrefix = "email-";
	// 手机号码缓存key前缀
	private String mobilePhoneNumberKeyPrefix = "mobilePhoneNumber-";

	/**
	 * 用户目标类切点
	 */
	@Pointcut(value = "target(com.apanal.qlife.sys.service.UserService)")
	public void userServicePointcut() {
	}

	/**
	 * evict, 比如删除
	 */
	@Pointcut(value = "execution(* update*(..)) || execution(* delete*(..)) || execution(* changePassword(..)) || execution(* resetPwd(..))")
	public void userCacheEvictPointcut() {
	}

	/**
	 * put或get, 比如查询
	 */
	@Pointcut(value = "(execution(* findByUsername(*)) "
			+ "|| execution(* findByEmail(*)) "
			+ "|| execution(* findByMobile(*)) "
			+ "|| execution(* selectById(*)))")
	public void userCacheablePointcut() {
	}

	/**
	 * 后置清除缓存通知
	 * 
	 * @param jp
	 */
	@Before(value = "userServicePointcut() && userCacheEvictPointcut()")
	public void userCacheEvictAfterAdvice(JoinPoint jp) {
		Object arg = jp.getArgs().length >= 1 ? jp.getArgs()[0] : null;

		if (arg == null) {
			return;
		}

		if (arg instanceof Long) {
			// only evict id
			evictId((Long) arg);
		} else if (arg instanceof Long[]) {
			for (Long id : (Long[]) arg) {
				// only evict id
				evictId(id);
			}
		} else if (arg instanceof User) {
			// evict user
			evict((User) arg);
		}
	}

	/**
	 * 环绕缓存或使用缓存通知
	 * 
	 * @param pjp
	 * @return
	 * @throws Throwable
	 */
	@Around(value = "userServicePointcut() && userCacheablePointcut()")
	public Object userCacheableAdvice(ProceedingJoinPoint pjp) throws Throwable {
		String methodName = pjp.getSignature().getName();
		Object arg = pjp.getArgs().length >= 1 ? pjp.getArgs()[0] : null;

		String key = "";
		boolean isIdKey = false;
		if ("selectById".equals(methodName)) {
			key = idKey((Long) arg);
			isIdKey = true;
		} else if ("findByUsername".equals(methodName)) {
			key = usernameKey((String) arg);
		} else if ("findByEmail".equals(methodName)) {
			key = emailKey((String) arg);
		} else if ("findByMobile".equals(methodName)) {
			key = mobilePhoneNumberKey((String) arg);
		}

		User user = null;
		if (isIdKey == true) {
			user = get(key);
		} else {
			Long id = get(key);
			if (id != null) {
				key = idKey(id);
				user = get(key);
			}
		}
		// cache hit
		if (user != null) {
			log.debug("cacheName:{}, hit key:{}", cacheName, key);
			return user;
		}

		// cache miss
		log.debug("cacheName:{}, miss key:{}", cacheName, key);
		user = (User) pjp.proceed();

		// put cache
		put(user);
		return user;

	}

	/**
	 * 根据用户id得到用户id缓存key
	 * 
	 * @param userId
	 * @return
	 */
	private String idKey(Long id) {
		return idKeyPrefix + id;
	}

	/**
	 * 根据用户名称得到用户名称缓存key
	 * 
	 * @param userId
	 * @return
	 */
	private String usernameKey(String username) {
		return usernameKeyPrefix + username;
	}

	/**
	 * 根据用户邮件得到用户邮件缓存key
	 * 
	 * @param userId
	 * @return
	 */
	private String emailKey(String email) {
		return emailKeyPrefix + email;
	}

	/**
	 * 根据用户手机得到用户手机缓存key
	 * 
	 * @param userId
	 * @return
	 */
	private String mobilePhoneNumberKey(String mobilePhoneNumber) {
		return mobilePhoneNumberKeyPrefix + mobilePhoneNumber;
	}

	/**
	 * 缓存用户对象
	 * 
	 * @param user
	 */
	public void put(User user) {
		if (user == null) {
			return;
		}
		Long id = user.getUseId();
		// username email mobilePhoneNumber ---> id
		put(usernameKey(user.getUseName()), id);
		put(emailKey(user.getUseEmail()), id);
		put(mobilePhoneNumberKey(user.getUseMobilePhone()), id);
		// id ---> user
		put(idKey(id), user);
	}

	/**
	 * 根据用户id移除对应缓存用户
	 * 
	 * @param id
	 */
	public void evictId(Long id) {
		evict(idKey(id));

		// 当用户过期时同时清理权限缓存
		userAuthCacheAspect.evict(id);
	}

	/**
	 * 根据用户对象移除用户所有缓存
	 * 
	 * @param user
	 */
	public void evict(User user) {
		if (user == null) {
			return;
		}
		Long id = user.getUseId();
		User u = userService.selectById(id);
		evictId(id);
		evict(usernameKey(u.getUseName()));
		evict(emailKey(u.getUseEmail()));
		evict(mobilePhoneNumberKey(u.getUseMobilePhone()));
	}

}
